﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

using SpriteSheetRuntime;

namespace Megaman
{

    //OUR BRAVE MAVERICK HUNTER!
    class Megaman
    {

        #region Content

        private AnimationPlayback<SNames> Playback;
        private AnimationPlayback<bulletTypes> Aura;

        private Animation mediumAura;
        private Animation bigAura;


        //Mi propio AnimatedStateManager... hace mas facil todos los estados de animacion
        //que toman tiempo de ralizarse. Es un generic que pide un enum con los estados
        private AnimatedStateManager<SNames> State = new AnimatedStateManager<SNames>();

        private Texture2D Ready;

        //Distintos Estados Posibles de Animación de Megaman
        public enum SNames
        {
            idle,
            running,
            dashing,
            chargingJump,
            jumpAccent,
            jumpDescent,
            landing,
            firing,
            crouching,
            injured,
            dying,
            materializing,
            started,
            teletransporting,
            mediumCharge,
            bigCharge,
            mediumFire,
            bigFire,
            all
        }

        public enum bulletTypes
        {
            normal,
            medium,
            big
        }
        //Cosas de la Vida
        private const int HP = 28;
        private int[] HealthValues = new int[] { HP, HP };
        private Vector2 hpPosition;
        private Texture2D HealthBar;
        private Texture2D HealthPoint;

        //Balas!!! ...   .         .                .
        private Animation NormalBusterBullet;
        private Animation MediumBusterBullet;
        private Animation FullPowerBusterBullet;

        private float FIRSTCHARGEWAITTIME = 0.3f;
        private const float NORMALBULLETVELOCITY = 2000f;
        private const float MEDIUMBULLETVELOCITY = 1500f;
        private const float FULLPOWERVELOCITY = 1000f;

        private const int NORMALBULLETDAMAGE = 1;
        private const int MEDIUMBULLETDAMAGE = 4;
        private const int FULLPOWERBULLETDAMAGE = 16;


        private Animation loadAnimations(SNames name)
        {
            switch (name)
            {
                case SNames.idle:
                    return new Animation(level.Content.Load<SpriteSheet>(
                "Sprites//Megaman//Idle//SpriteSheetIdle"),
                "1", 5, new Vector2(100, 140), 7, true, new Rectangle(14, 29, 50, 90), new Vector2(50, 120));

                case SNames.running:
                    return new Animation(level.Content.Load<SpriteSheet>(
               "Sprites//Megaman//Running//SpriteSheetRunning"),
               "3", 14, new Vector2(100, 120), 25, true, new Rectangle(14, 29, 50, 90), new Vector2(50, 120));

                case SNames.dashing:
                    return new Animation(level.Content.Load<SpriteSheet>(
               "Sprites//Megaman//Running//SpriteSheetRunning"),
               "3", 14, new Vector2(100, 120), 32, true, new Rectangle(14, 29, 50, 90), new Vector2(50, 120));

                case SNames.jumpAccent:
                    return new Animation(level.Content.Load<SpriteSheet>(
               "Sprites//Megaman//Jump//Accent//SpriteSheetAccent"),
               "1", 4, new Vector2(100, 120), 20, false, new Rectangle(14, 29, 50, 90), new Vector2(50, 120));

                case SNames.jumpDescent:
                    return new Animation(level.Content.Load<SpriteSheet>(
               "Sprites//Megaman//Jump//Descent//SpriteSheetDescent"),
              "1", 4, new Vector2(100, 120), 20, false, new Rectangle(14, 29, 50, 90), new Vector2(50, 120));

                case SNames.materializing:
                    return new Animation(level.Content.Load<SpriteSheet>(
                "Sprites//Megaman//Starting//Materializing//SpriteSheetMaterializing"),
                "1", 15, new Vector2(200, 200), 16, false, new Rectangle(57, 23, 79, 90), new Vector2(98, 114));

                case SNames.bigFire:
                    return new Animation(level.Content.Load<SpriteSheet>(
                "Sprites//Megaman//Firing//Standing//SpriteSheetFiringStandingFullPower"),
                "a", 8, new Vector2(200, 156), 16, false, new Rectangle(69, 55, 77, 85), new Vector2(100, 141));

                case SNames.mediumFire :
                    return new Animation(level.Content.Load<SpriteSheet>(
                        "Sprites//Megaman//Firing//Standing//SpriteSheetFiringStandingMediumPower"),
                        "m1", 7, new Vector2(160, 120), 18, false, new Rectangle(45, 31, 65, 85), new Vector2(80, 117));

                case SNames.dying:
                    return new Animation(level.Content.Load<SpriteSheet>(
                  "Sprites//Megaman//Killed//SpriteSheetKilled"),
                  "1", 5, new Vector2(50, 50), 2, true, new Rectangle(25, 25, 1, 1), new Vector2(25, 25));

                case SNames.firing:
                    return new Animation(level.Content.Load<SpriteSheet>(
                "Sprites//Megaman//Firing//Standing//SpriteSheetFiringStanding"),
                "1", 8, new Vector2(140, 120), 32, false, new Rectangle(30, 31, 77, 85), new Vector2(66, 117));

                case SNames.landing:
                    return new Animation(level.Content.Load<SpriteSheet>(
                "Sprites//Megaman//Jump//Landing//SpriteSheetLanding"),
               "1", 3, new Vector2(100, 140), 16, false, new Rectangle(14, 29, 50, 90), new Vector2(50, 120));

                case SNames.injured:
                    return new Animation(level.Content.Load<SpriteSheet>(
                "Sprites//Megaman//Injured//SpriteSheetInjured"),
               "1", 4, new Vector2(100, 120), 20, true, new Rectangle(14, 29, 50, 90), new Vector2(50, 120));

                case SNames.teletransporting:
                    return new Animation(level.Content.Load<SpriteSheet>(
                "Sprites//Megaman//Starting//Laser//SpriteSheetLaser"),
                "1", 2, new Vector2(200, 200), 100, true, new Rectangle(87, 5, 20, 164), new Vector2(96, 168));

                case SNames.crouching:
                    return new Animation(level.Content.Load<SpriteSheet>(
                 "Sprites//Megaman//Crouching//SpriteSheetCrouching"),
                 "1", 2, new Vector2(100, 120), 16, false, new Rectangle(8, 50, 64, 63), new Vector2(50, 112));

                
                    
                default: return null;
            }
        }

        private SoundEffect loadSoundEffects(SNames name)
        {
            SoundEffect none = null;
            switch (name)
            {
                case SNames.materializing:
                    none = level.Content.Load<SoundEffect>(
            "Audio//SE//Megaman//Materializing");
                    break;
                case SNames.teletransporting:
                    none = level.Content.Load<SoundEffect>(
                "Audio//SE//Megaman//Laser");
                    break;
                case SNames.injured:
                    none = level.Content.Load<SoundEffect>(
                "Audio//SE//Megaman//Injured");
                    break;
                case SNames.jumpAccent:
                    none = level.Content.Load<SoundEffect>(
                "Audio//SE//Megaman//Jump");
                    break;
                case SNames.dashing:
                    none = level.Content.Load<SoundEffect>(
                            "Audio//SE//Megaman//Dashing");
                    break;
                case SNames.dying:
                    none = level.Content.Load<SoundEffect>(
                "Audio//SE//Megaman//Dying");
                    break;
                case SNames.firing:
                    none = level.Content.Load<SoundEffect>(
                "Audio//SE//Megaman//BlasterShot");
                    break;

                default:
                    break;
            }
            return none;
        }

        private void loadTextures()
        {
            Ready = level.Content.Load<Texture2D>("Sprites\\Megaman\\Starting\\Ready");

            HealthBar = level.Content.Load<Texture2D>("Sprites\\Megaman\\Health\\HealthBar");

            HealthPoint = level.Content.Load<Texture2D>("Sprites\\Megaman\\Health\\HealthPoint");
        }

        private void loadState(SNames name)
        {
            switch (name)
            {
                case SNames.started:
                    State.Add(SNames.started, 4f, null, null);

                    break;
                case SNames.bigCharge:

                    State.Add(SNames.bigCharge, 1f,
                                loadAnimations(SNames.bigCharge),
                                loadSoundEffects(SNames.bigCharge));

                    break;
                case SNames.mediumCharge:
                    State.Add(SNames.mediumCharge, 1f + FIRSTCHARGEWAITTIME,
                                loadAnimations(SNames.mediumCharge),
                                loadSoundEffects(SNames.mediumCharge));
                    break;
                case SNames.bigFire:

                    State.Add(SNames.bigFire, 0.6f,
                                loadAnimations(SNames.bigFire),
                                loadSoundEffects(SNames.bigFire));

                    break;
                case SNames.mediumFire:
                    State.Add(SNames.mediumFire, 0.36f,
                                loadAnimations(SNames.mediumFire),
                                loadSoundEffects(SNames.mediumFire));
                    break;
                case SNames.crouching:

                    State.Add(SNames.crouching,
                            0.2f,
                                loadAnimations(SNames.crouching),
                                loadSoundEffects(SNames.crouching));

                    break;
                case SNames.dying:

                    State.Add(SNames.dying,
                            4f,
                                loadAnimations(SNames.dying),
                                loadSoundEffects(SNames.dying));


                    break;
                case SNames.firing:

                    State.Add(SNames.firing,
                            0.3f,
                                loadAnimations(SNames.firing),
                                loadSoundEffects(SNames.firing));

                    break;
                case SNames.idle:

                    State.Add(SNames.idle,
                            0,
                                loadAnimations(SNames.idle),
                                loadSoundEffects(SNames.idle));

                    break;
                case SNames.injured:

                    State.Add(SNames.injured,
                            1f,
                                loadAnimations(SNames.injured),
                                loadSoundEffects(SNames.injured));

                    break;
                case SNames.jumpAccent:

                    State.Add(SNames.jumpAccent,
                            maxAscensionTime,
                                loadAnimations(SNames.jumpAccent),
                                loadSoundEffects(SNames.jumpAccent));

                    break;
                case SNames.jumpDescent:

                    State.Add(SNames.jumpDescent,
                            0,
                                loadAnimations(SNames.jumpDescent),
                                loadSoundEffects(SNames.jumpDescent));

                    break;
                case SNames.landing:
                case SNames.all:
                    State.Add(SNames.landing,
                            0.23f,
                                loadAnimations(SNames.landing),
                                loadSoundEffects(SNames.landing));

                    break;
                case SNames.materializing:

                    State.Add(SNames.materializing,
                            1.3f,
                                loadAnimations(SNames.materializing),
                                loadSoundEffects(SNames.materializing));

                    break;

                case SNames.running:

                    State.Add(SNames.running,
                            0,
                                loadAnimations(SNames.running),
                                loadSoundEffects(SNames.running));

                    break;
                case SNames.dashing:

                    State.Add(SNames.dashing,
                            0,
                                loadAnimations(SNames.dashing),
                                loadSoundEffects(SNames.dashing));

                    break;
                case SNames.teletransporting:

                    State.Add(SNames.teletransporting,
                            0,
                                loadAnimations(SNames.teletransporting),
                                loadSoundEffects(SNames.teletransporting));

                    break;

                case SNames.chargingJump:

                    State.Add(SNames.chargingJump,
                            maxAscensionTime,
                                loadAnimations(SNames.chargingJump),
                                loadSoundEffects(SNames.chargingJump));
                    break;
             
                default: break;
                    
            }
            return;
        }

        private void loadStates(SNames name)
        {
          
            if(name != SNames.all )
                loadState(name);
            else
            {
                foreach(SNames n in Enum.GetValues(typeof(SNames)))
                {
                    if (n != SNames.all)
                    {
                        loadState(n);
                    }
                }
            }

        }

        private void loadBulletAnimations()
        {
            NormalBusterBullet = new Animation(
                level.Content.Load<SpriteSheet>(
                "Sprites//Megaman//Firing//Bullets//Normal//SpriteSheetNormalBullet"), "6", 5, new Vector2(60, 60),
                 8, false, new Rectangle(18, 21, 32, 21), new Vector2(36, 36));

            MediumBusterBullet = new Animation(
                level.Content.Load<SpriteSheet>(
                "Sprites//Megaman//Firing//Bullets//Medium//SpriteSheetMediumfire"), "bullet1", 4, new Vector2(60, 40),
                 8, false, new Rectangle(1, 2, 55, 37), new Vector2(38, 20));

            FullPowerBusterBullet = new Animation(
                level.Content.Load<SpriteSheet>(
                "Sprites//Megaman//Firing//Bullets//FullPower//SpriteSheetFullBullet"), "bullet1", 3, new Vector2(120, 60),
                8, false, new Rectangle(0, 0, 120, 60), new Vector2(60, 60));
        }

        private void loadAuraAnimations()
        {
            mediumAura = new Animation(level.Content.Load<SpriteSheet>(
                "Sprites//Megaman//Firing//Aura//Medium//SpriteSheetAuraMedium"), "1", 8, new Vector2(300, 300), 30,
                true, new Rectangle(0, 0, 300, 300), new Vector2(150, 150));

            

        }
        public void LoadContent()
        {

            loadTextures();

            hpPosition = new Vector2(10, 100);

            loadStates(SNames.all);

            loadBulletAnimations();

            loadAuraAnimations();


        }


        #endregion

        #region Level

        public Level Level
        {
            get { return level; }
        }
        Level level;

        public bool IsAlive
        {
            get { return isAlive; }
        }
        bool isAlive;


        private Rectangle localBounds;
        /// <summary>
        /// Gets a rectangle which bounds this player in world space.
        /// </summary>
        public Rectangle BoundingRectangle
        {
            get
            {
                int left = (int)Math.Round(Position.X - Playback.Origin().X) + localBounds.X;
                int top = (int)Math.Round(Position.Y - Playback.Origin().Y) + localBounds.Y;

                if (Playback.State == SNames.bigCharge || Playback.State == SNames.mediumCharge ||
                    Playback.State == SNames.firing)
                {
                    int a = 3;
                }
                return new Rectangle(left, top, localBounds.Width, localBounds.Height);
            }
        }

        /// <summary>
        /// Current user movement input.
        /// </summary>
       
        private float heading;
        // Jumping state
        private bool isJumping;

        public Megaman(Level level, Vector2 Position)
        {
            this.level = level;
            //Hay que calcularlo antes de cargarlo en content
            maxAscensionTime = -(MaxJumpVelocity / Gravity)/2;
            LoadContent();
            Reset(Position);


        }

        public void Reset(Vector2 Position)
        {

            this.Position = Position;
            Velocity = Vector2.Zero;
            isAlive = true;
            Playback.PlayAnimation(State.getAnimation(SNames.teletransporting),SNames.teletransporting);
            localBounds = Playback.LocalBounds();
        }

        #endregion

        #region Physics

        public Vector2 Position
        {
            get { return position; }
            set { position = value; }
        }
        Vector2 position;

        private float previousBottom;

        public Vector2 Velocity
        {
            get { return velocity; }
            set { velocity = value; }
        }
        Vector2 velocity;


        /// <summary>
        /// Gets whether or not the player's feet are on the ground.
        /// </summary>
        public bool IsOnGround
        {
            get { return isOnGround; }
        }
        bool isOnGround;


        private Vector2 movement = new Vector2(0, 1);
        //Velocidad en X al correr, diez tiles por segundo.
        private float RunningVelocity = Tile.Width * 10;
        //Velocidad en Y al saltar, la altura de megaman * 9
        private float MaxJumpVelocity = -160*7;


        private float Gravity = 2000f;
        private const float MaxFallSpeed = 2000.0f;

        //Tiempo max de ascenso de un salto
        private float maxAscensionTime;


        public void ApplyPhysics(GameTime gameTime)
        {
            
            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;

            //La ecuacion de Fisica 1 de Diaz
            // X = Xo + Vo(t - to)
            //En caso de estar acelerado constantemente
            // X = Xo + Vo(t - to) + a/2(t - t0)^2

            Vector2 previousPosition = Position;

            velocity.X = RunningVelocity*movement.X;
            velocity.Y = MathHelper.Clamp(velocity.Y + Gravity * elapsed, -MaxFallSpeed, MaxFallSpeed);

            DoJump(velocity.Y, gameTime);
            

            // Apply velocity.


            Position += velocity * elapsed;
            Position = new Vector2((float)Math.Round(Position.X), (float)Math.Round(Position.Y));

            // If the player is now colliding with the level, separate them.
            HandleCollisions();

            if (position.X == previousPosition.X)
                velocity.X = 0;
            if (position.Y == previousPosition.Y)
                velocity.Y = 0;




        }

        
        //Calcula la velocidad en Y
        private void DoJump(float velocityY, GameTime gameTime)
        {
            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
            float accel;
            
            if (isJumping)
            {
                if (isOnGround)
                    State.updateTimer(SNames.chargingJump, elapsed);
                else
                    State.resetState(SNames.chargingJump);
            }
            else
            {
                if (State.hasStarted(SNames.chargingJump))
                {
                    if (isOnGround)
                    {
                        velocityY = MaxJumpVelocity * MathHelper.Clamp((State.getElapsedTime(SNames.chargingJump)/
                            State.getTime(SNames.chargingJump)),0,1)*
                            movement.Y;
                       
                    }
                    State.resetState(SNames.chargingJump);
                }
            }

            //Calculo la Velocidad con la aceleracion aqui mismo
            //V = Vo + a(t-to)
            if (isOnGround)
                accel = 0;
            else
                accel = Gravity;

           velocity.Y = MathHelper.Clamp( velocityY + accel * elapsed,-MaxFallSpeed,MaxFallSpeed);
       

            
        }

        /// <summary>
        /// Detects and resolves all collisions between the player and his neighboring
        /// tiles. When a collision is detected, the player is pushed away along one
        /// axis to prevent overlapping. There is some special logic for the Y axis to
        /// handle platforms which behave differently depending on direction of movement.
        /// </summary>
        private void HandleCollisions()
        {
            // Get the player's bounding rectangle and find neighboring tiles.
            Rectangle bounds = BoundingRectangle;
            int leftTile = (int)Math.Floor((float)bounds.Left / Tile.Width);
            int rightTile = (int)Math.Ceiling(((float)bounds.Right / Tile.Width)) - 1;
            int topTile = (int)Math.Floor((float)bounds.Top / Tile.Height);
            int bottomTile = (int)Math.Ceiling(((float)bounds.Bottom / Tile.Height)) - 1;

            // Reset flag to search for ground collision.
            isOnGround = false;

            // For each potentially colliding tile,
            for (int y = topTile; y <= bottomTile; ++y)
            {
                for (int x = leftTile; x <= rightTile; ++x)
                {
                    // If this tile is collidable,
                    TileCollision collision = Level.GetCollision(x, y);
                    if (collision != TileCollision.Passable)
                    {
                        // Determine collision depth (with direction) and magnitude.
                        Rectangle tileBounds = Level.GetBounds(x, y);
                        Vector2 depth = RectangleExtensions.GetIntersectionDepth(bounds, tileBounds);
                        if (depth != Vector2.Zero)
                        {
                            float absDepthX = Math.Abs(depth.X);
                            float absDepthY = Math.Abs(depth.Y);

                            // Resolve the collision along the shallow axis.
                            if (absDepthY < absDepthX || collision == TileCollision.Platform)
                            {
                                // If we crossed the top of a tile, we are on the ground.
                                if (previousBottom <= tileBounds.Top)
                                    isOnGround = true;

                                // Ignore platforms, unless we are on the ground.
                                if (collision == TileCollision.Impassable || IsOnGround)
                                {
                                    // Resolve the collision along the Y axis.
                                    Position = new Vector2(Position.X, Position.Y + depth.Y);

                                    // Perform further collisions with the new bounds.
                                    bounds = BoundingRectangle;
                                }
                            }
                            else if (collision == TileCollision.Impassable) // Ignore platforms.
                            {
                                // Resolve the collision along the X axis.
                                Position = new Vector2(Position.X + depth.X, Position.Y);

                                // Perform further collisions with the new bounds.
                                bounds = BoundingRectangle;
                            }
                        }
                    }
                }
            }

            // Save the new bounds bottom.
            previousBottom = bounds.Bottom;
        }


        #endregion

        #region Update
        public void Update(GameTime gameTime)
        {

            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;

            //Si todavia no me materialize sigo en la parte inicial
            if (!State.hasFinished(SNames.materializing))
            {
                StartingMegaman(gameTime);
                return;

            }

            //Si me estoy muriendo
            if (State.hasStarted(SNames.dying))
            {
                if (!State.updateTimer(SNames.dying, elapsed))
                {
                    isAlive = false;
                }
                return;
            }

            //Si me estan hiriendo
            if (State.hasStarted(SNames.injured))
            {
                if (!State.updateTimer(SNames.injured, elapsed))
                {
                    State.resetState(SNames.injured);
                }
                return;
            }

            //Si estoy saltando
            if (!isOnGround)
                UpdateMegamanJumping(gameTime);
            
            //Estoy normal, entonces recibo input y aplico fisica
            GetInput();

            ApplyPhysics(gameTime);

            //Si estoy en el piso soy capaz de empezar una nueva accion
            if (IsAlive && IsOnGround)
                UpdateMegamanStanding(gameTime);



            if (isFiring)
                UpdateMegamanFiring(gameTime);

            UpdateAura(gameTime);
            // Clear input.
            movement.X = 0.0f;
            movement.Y = 1f;
            isJumping = false;
            isCrouching = false;
            State.resetState(SNames.crouching);
      
            fireButtonReleased = false;


        }


        private void StartingMegaman(GameTime gameTime)
        {
            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;

            //Si el cartel de READY sigue activo me voy
            if (State.updateTimer(SNames.started, elapsed))
                return;

            //Comienzo a hacer caer a Megaman hecho laser, "teletransporta"
            ApplyPhysics(gameTime);


            // Al tocar el piso me materializo
            if (isOnGround)
            {

                Playback.PlayAnimation(State.getAnimation(SNames.materializing),SNames.materializing);
                localBounds = Playback.LocalBounds();

                if (State.updateTimer(SNames.materializing, elapsed))
                {
                    if (!State.hasTheSoundPlayed(SNames.materializing) && (
                    State.getElapsedTime(SNames.materializing) > 0.4f))
                        State.playSound(SNames.materializing);
                }
                //Ya me materialize
                else
                {
                    Playback.PlayAnimation(State.getAnimation(SNames.idle),SNames.idle);
                    localBounds = Playback.LocalBounds();
                }
                

            }

            return;
        }

        private void UpdateMegamanJumping(GameTime gameTime)
        {
            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
            //Si estoy subiendo
            //OJO GLITCH QUICK FIX A VECES SALTA CORRIENDO O AGACHADO
            if (Velocity.Y <= 0)
            {
                if (Playback.Animation != State.getAnimation(SNames.jumpAccent))
                {
                    State.resetState(SNames.jumpAccent);
                }
                if (!State.hasStarted(SNames.jumpAccent))
                {
                    State.playSound(SNames.jumpAccent);
                    Playback.PlayAnimation(State.getAnimation(SNames.jumpAccent), SNames.jumpAccent);
                    localBounds = Playback.LocalBounds();
                }
                State.updateTimer(SNames.jumpAccent, elapsed);

            }
            //Estoy cayendo
            else if (Velocity.Y > 0)
            {
                if (Playback.Animation != State.getAnimation(SNames.jumpDescent))
                {
                    State.resetState(SNames.jumpDescent);
                }
                if (!State.hasStarted(SNames.jumpDescent))
                {
                    Playback.PlayAnimation(State.getAnimation(SNames.jumpDescent), SNames.jumpDescent);
                    localBounds = Playback.LocalBounds();
                }
                State.updateTimer(SNames.jumpDescent, elapsed);

            }
        }

        private void UpdateMegamanStanding(GameTime gameTime)
        {
            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;

            //Si me agacho no me puedo mover
            if (State.hasStarted(SNames.crouching))
            {
                Playback.PlayAnimation(State.getAnimation(SNames.crouching),SNames.crouching);
                localBounds = Playback.LocalBounds();
            }
            else
            {
                State.resetState(SNames.crouching);
                float absVel_X = Math.Abs(Velocity.X);

                //Caminando
                if (absVel_X != 0 && absVel_X <= RunningVelocity )
                {
                    State.resetState(SNames.dashing);
                    State.resetState(SNames.firing);
                    State.resetState(SNames.mediumFire);
                    State.resetState(SNames.bigFire);
                    Playback.PlayAnimation(State.getAnimation(SNames.running),SNames.running);
                    localBounds = Playback.LocalBounds();
                    
                }
                //Corriendo a toda
                else if (absVel_X > RunningVelocity )
                {
                    State.resetState(SNames.firing);
                    State.resetState(SNames.mediumFire);
                    State.resetState(SNames.bigFire);   
                    Playback.PlayAnimation(State.getAnimation(SNames.dashing),SNames.dashing);
                    localBounds = Playback.LocalBounds();
                    if (!State.hasTheSoundPlayed(SNames.dashing))
                        State.playSound(SNames.dashing);
                }
   
                //Aqui o estoy tranquilo, o me estoy reincorporando de un salto completo 
                // o estoy a punto de disparar

                else
                {
                    //Para que el sonido vuelva a no ser tocado
                    State.resetState(SNames.dashing);

                    //Si realice un salto que llego al maximo de altura
                    if (State.hasFinished(SNames.jumpAccent))
                    {
                        if (!State.hasStarted(SNames.landing))
                        {
                            Playback.PlayAnimation(State.getAnimation(SNames.landing),SNames.landing);
                            localBounds = Playback.LocalBounds();

                        }
                        //Si ya me termine de reincorporar reseteo los saltos y me paro erguido
                        if (!State.updateTimer(SNames.landing, elapsed))
                        {
                            State.resetState(SNames.jumpAccent);
                            State.resetState(SNames.jumpDescent);
                            State.resetState(SNames.landing);
                            Playback.PlayAnimation(State.getAnimation(SNames.idle),SNames.idle);
                            localBounds = Playback.LocalBounds();
                        }

                    }
                    else 
                    {
                        State.resetState(SNames.jumpAccent);
                        State.resetState(SNames.jumpDescent);
                        State.resetState(SNames.landing);
                        if (!State.hasStarted(SNames.firing) && !State.hasStarted(SNames.bigFire)
                            &&!State.hasStarted(SNames.mediumFire))
                        {
                            Playback.PlayAnimation(State.getAnimation(SNames.idle), SNames.idle);
                            localBounds = Playback.LocalBounds();
                        }
                    }
                }
            }



        }


        private void UpdateMegamanFiring(GameTime gametime)
        {
            SNames name = Playback.State;
            float elapsed = (float)gametime.ElapsedGameTime.TotalSeconds;

            if (fireButtonPressed)
            {
                if (!State.updateTimer(SNames.mediumCharge, elapsed))
                {
                    State.updateTimer(SNames.bigCharge, elapsed);
                }
            }
           
            switch (name)
            {
                case SNames.idle:
                    {
                        if (fireButtonReleased)
                        {
                            if (!State.hasFinished(SNames.mediumCharge))
                                State.startState(SNames.firing);
                            else
                            {
                                if (!State.hasFinished(SNames.bigCharge))
                                {
                                    Playback.PlayAnimation(State.getAnimation(SNames.mediumFire), SNames.mediumFire);
                                    localBounds = Playback.LocalBounds();
                                    State.startState(SNames.mediumFire);
                                    State.playSound(SNames.firing);
                                    FireStandingUp(gametime,bulletTypes.medium);
                                }
                                else
                                {
                                    Playback.PlayAnimation(State.getAnimation(SNames.bigFire), SNames.bigFire);
                                    localBounds = Playback.LocalBounds();
                                    State.startState(SNames.mediumFire);
                                    State.playSound(SNames.firing);
                                    FireStandingUp(gametime,bulletTypes.big);
                                }
                            }
                        }

                        if (State.hasStarted(SNames.firing))
                        {
                            if (!State.hasTheSoundPlayed(SNames.firing))
                            {
                                Playback.PlayAnimation(State.getAnimation(SNames.firing), SNames.firing);
                                localBounds = Playback.LocalBounds();
                                State.playSound(SNames.firing);
                                FireStandingUp(gametime, bulletTypes.normal);
                            }
                        }        

                    }
                    break;
                case SNames.firing:
                    {

                        if (!State.updateTimer(SNames.firing, elapsed))
                        {
                            isFiring = false;
  
                        }
                        if (fireButtonReleased)
                        {
                            State.resetState(SNames.firing);
                            State.startState(SNames.firing);
                            Playback.PlayAnimation(State.getAnimation(SNames.idle), SNames.idle);
                            Playback.PlayAnimation(State.getAnimation(SNames.firing), SNames.firing);
                            localBounds = Playback.LocalBounds();
                            State.playSound(SNames.firing);
                            FireStandingUp(gametime,bulletTypes.normal);
                        }
                    }
                    break;
                case SNames.mediumFire:
                    {
                        if (!State.updateTimer(SNames.mediumFire, elapsed))
                        {
                            isFiring = false;
                        }
                        if (fireButtonPressed)
                        {
                            State.resetState(SNames.mediumCharge);
                            State.resetState(SNames.bigCharge);
                            State.startState(SNames.firing);
                        }
                    }
                    break;
                case SNames.bigFire:
                    {
                        if (!State.updateTimer(SNames.bigFire, elapsed))
                        {
                            isFiring = false;
                        }
                    }
                    break;
                default:
                    {
                        if (fireButtonReleased || (!fireButtonPressed && !fireButtonReleased))
                        {
                            isFiring = false;
                        }
                        
                    }

                    break;
            }
            if (isFiring == false )
            {
                State.resetState(SNames.firing);
                State.resetState(SNames.mediumCharge);
                State.resetState(SNames.bigCharge);
                State.resetState(SNames.mediumFire);
                State.resetState(SNames.bigFire);

            }
        }

        private void UpdateAura(GameTime gameTime)
        {
            if (fireButtonPressed)
            {
                if (!State.hasFinished(SNames.mediumCharge) && State.getElapsedTime(SNames.mediumCharge)  > FIRSTCHARGEWAITTIME)
                {
                    Aura.PlayAnimation(mediumAura, bulletTypes.medium);
                }
                else
                    Aura.PlayAnimation(null, bulletTypes.normal);
            }
            else
                Aura.PlayAnimation(null, bulletTypes.normal);

        }
        private void FireStandingUp(GameTime gameTime, bulletTypes type)
        {
            int x_displacement= 0;
            int y_displacement=0;
            bool set = false;
            Animation thisBullet = null;
            float velocity =0;
            int damage=0;

            switch (type)
            {
                case bulletTypes.normal:
                    {
                        x_displacement = 60;
                        y_displacement = -55;
                        thisBullet = NormalBusterBullet;
                        velocity = NORMALBULLETVELOCITY;
                        damage = NORMALBULLETDAMAGE;

                        set = true;
                        
                    }
                    break;
                case bulletTypes.medium:
                    {
                        x_displacement = 60;
                        y_displacement = -59;
                        thisBullet = MediumBusterBullet;
                        damage = MEDIUMBULLETDAMAGE;
                        velocity = MEDIUMBULLETVELOCITY;


                        set = true;

                    }
                    break;
                case bulletTypes.big:
                    {
                        x_displacement = 60;
                        y_displacement = -30;
                        thisBullet = FullPowerBusterBullet;
                        damage = FULLPOWERBULLETDAMAGE;
                        velocity = FULLPOWERVELOCITY;

                        set = true;

                    }
                    break;
                default: set = false;
                    break;
            }
            if (set)
            {
                Bullet.Heading bulletHeading;
                if (heading == -1)
                {
                    x_displacement *= -1;
                    bulletHeading = Bullet.Heading.Left;
                }
                else
                {
                    bulletHeading = Bullet.Heading.Right;
                }

                level.BulletFired(new Bullet(thisBullet, new Vector2(
                            position.X + x_displacement, position.Y + y_displacement), bulletHeading, velocity, damage));
            }
        

        }



        private void InjureMegaman()
        {
            State.startState(SNames.injured);
            Playback.PlayAnimation(State.getAnimation(SNames.injured),SNames.injured);
            State.playSound(SNames.injured);
        }

        public void Hit(Enemy enemyHitBy)
        {
            if (!State.hasStarted(SNames.injured))
            {
                UpdateHealthBar(enemyHitBy.Damage);

            }



        }

        public void Killed()
        {
            State.startState(SNames.dying);
            Playback.PlayAnimation(State.getAnimation(SNames.dying),SNames.dying);
            State.playSound(SNames.dying);

        }

        public bool exploding()
        {
            return State.stillPlaying(SNames.dying);
        }
        private int xOffset = 8;

        private void UpdateHealthBar(int hp)
        {
            HealthValues[1] += hp;
            if (HealthValues[1] > HealthValues[0])
                HealthValues[1] = HealthValues[0];
            if (HealthValues[1] <= 0)
            {
                if (!State.hasStarted(SNames.dying))
                {
                    HealthValues[1] = 0;
                    Killed();
                }
            }
            else
            {
                InjureMegaman();
            }

        }

        #endregion

        #region Input

        // Input configuration
        private const float MoveStickScale = 1.0f;
        private const Buttons JumpButton = Buttons.A;
        private bool isCrouching;
        private bool isFiring;
        private bool fireButtonPressed;
        private bool fireButtonReleased;


        public void GetInput()
        {
            // Get input state.
            KeyboardState keyboardState = Keyboard.GetState();
            GamePadState gamepadState = GamePad.GetState(PlayerIndex.One);


            movement.X = gamepadState.ThumbSticks.Left.X * MoveStickScale;

            float yaxis = gamepadState.ThumbSticks.Left.Y * MoveStickScale;

            if (movement.X < 0)
                heading = -1f;
            else if (movement.X > 0)
                heading = 1f;

            if (Math.Abs(movement.X) < 0.5f)
                movement.X = 0.0f;


            //Si ingresa alguna tecla digital,(teclado o Dpad, entonces override lo analogico
            if (keyboardState.IsKeyDown(Keys.Right) || keyboardState.IsKeyDown(Keys.D) ||
                gamepadState.IsButtonDown(Buttons.DPadRight))
            {
                movement.X = movement.Y = 1;
                heading = 1f;
            }


            if (keyboardState.IsKeyDown(Keys.Left) || keyboardState.IsKeyDown(Keys.A) ||
                gamepadState.IsButtonDown(Buttons.DPadLeft))
            {
                movement.X = movement.Y = -1;
                heading = -1f;
            }

            if (keyboardState.IsKeyDown(Keys.LeftShift) || gamepadState.IsButtonDown(Buttons.LeftStick))
            {
                movement.X *= 2f;
                movement.Y = 1.5f;
            }

            // Check if the player wants to jump.
            isJumping = gamepadState.IsButtonDown(Buttons.A) ||
                keyboardState.IsKeyDown(Keys.Up) ||
                keyboardState.IsKeyDown(Keys.W);

           if ((keyboardState.IsKeyDown(Keys.Down) || keyboardState.IsKeyDown(Keys.S) ||
                gamepadState.IsButtonDown(Buttons.DPadDown) ||
                (yaxis < -0.9f)) && isOnGround)
            {
                State.startState(SNames.crouching);
            }

            if ((gamepadState.IsButtonDown(Buttons.X)))
            {

                isFiring = true;
                this.fireButtonPressed = true;
                this.fireButtonReleased = false;

            }
            
            if ((gamepadState.IsButtonUp(Buttons.X)) && fireButtonPressed)
            {
                this.fireButtonReleased = true;
                fireButtonPressed = false;

            }
        }


        #endregion

        #region Draw

        SpriteEffects effects;

        public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
        {
            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
            if (!State.hasFinished(SNames.started))
            {
                if ((int)State.getElapsedTime(SNames.started) % 2 == 0)
                    spriteBatch.Draw(Ready, new Rectangle(spriteBatch.GraphicsDevice.Viewport.Width / 2 - 50,
                        spriteBatch.GraphicsDevice.Viewport.Height / 2, 157, 25), Color.White);
                return;

            }

            if (heading < 0)
                effects = SpriteEffects.FlipHorizontally;
            else
                effects = SpriteEffects.None;

            if (State.stillPlaying(SNames.dying))
                DrawExplosion(spriteBatch, gameTime);
            else
            {
                DrawHealthBar(spriteBatch);
                if (Aura.State == bulletTypes.normal)
                {  DrawAura(gameTime, spriteBatch, Position, effects);

                    Playback.Draw(gameTime, spriteBatch, Position, effects);
                }
                else
                    DrawAura(gameTime, spriteBatch, Position, effects);
            }


        }

        private void DrawExplosion(SpriteBatch spriteBatch, GameTime gametime)
        {
            float radius = State.getElapsedTime(SNames.dying); ;
            radius *= 200;


            float angle = (2 * (float)Math.PI) / 8;
            int xoffset;
            int yoffset;
            for (int i = 0; i < 8; i++)
            {
                xoffset = (int)(Math.Sin(angle * i) * radius);
                yoffset = (int)(Math.Cos(angle * i) * radius);

                Playback.Draw(gametime, spriteBatch, new Vector2(Position.X + xoffset, Position.Y - 50 + yoffset), SpriteEffects.None);
                Playback.Draw(gametime, spriteBatch, new Vector2(Position.X + xoffset * 2, Position.Y - 50 + yoffset * 2), SpriteEffects.None);
            }

        }
        private void DrawHealthBar(SpriteBatch spriteBatch)
        {
            spriteBatch.Draw(HealthBar, new Vector2(hpPosition.X + level.cameraPosition.X, hpPosition.Y + level.cameraPosition.Y), Color.White);
            int i;
            int j = 120;
            for (i = HealthValues[1]; i > 0; i--)
            {
                j -= 4;
                spriteBatch.Draw(HealthPoint, new Vector2(hpPosition.X + xOffset + level.cameraPosition.X, hpPosition.Y + j + level.cameraPosition.Y), Color.White);

            }
        }
        float rotation = 0;
        int counter = 0;
        Color color = new Color(255,255,255,255);
        private void DrawAura(GameTime gameTime, SpriteBatch spriteBatch, Vector2 Position, SpriteEffects effects)
        {
            counter++;
            rotation += (float)(gameTime.ElapsedGameTime.TotalSeconds) * 15;
            rotation %= (360);
            
           // rotation = (rotation * 2 * (float)Math.PI) / 360;
            if (Aura.State == bulletTypes.normal)
            {
                rotation = 0;
                resetColor();
                return;
            }
            else
            {
                if (Aura.State == bulletTypes.medium)
                {
                    
                    Aura.Draw(gameTime, spriteBatch, new Vector2(Position.X,Position.Y - 40),rotation, effects);
                    

                    Playback.Draw(gameTime, spriteBatch, Position, 0f, effects, color);
                }
                else
                {
                    resetColor();
                    return;
                }

            }
        }

    
        float[] colors = { 255, 255,255, 255 };
        float step = 0;

        private Color getColorBlueish()
        {

            step += 0.05f;
            if (step >= 1)
                step = 1;
            float color;
            
            color = MathHelper.Lerp(255, 0, step);
            return new Color(color, color, 255, 255);
           

        }

        private Color getColorBlackish()
        {
            step += 0.05f;
            if (step >= 1)
                step = 1;
            float color;

            color = MathHelper.Lerp(255, 0, step);
            return new Color(color, color, color, 255);
        }
        private void resetColor()
        {
            Color color = new Color(255, 255, 255, 255);
        }
        #endregion
    }
}
